package com.lonefennec.chameleon.characters;

import java.util.HashSet;
import java.util.Set;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Filter;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.MassData;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.Disposable;
import com.lonefennec.chameleon.ChameleonGame;
import com.lonefennec.chameleon.Physics;

public abstract class WalkingActor extends Actor implements Disposable
{
	private Fixture footFixture;
	private Fixture leftFixture;
	private Fixture rightFixture;
	
	protected Body body;
	
	public Fixture getFootFixture()
	{
		return footFixture;
	}
	
	public Fixture getLeftFixture()
	{
		return leftFixture;
	}
	
	public Fixture getRightFixture()
	{
		return rightFixture;
	}
	
	public void setFootFixture(Fixture fixture)
	{
		footFixture = fixture;
	}
	
	public void setLeftFixture(Fixture fixture)
	{
		leftFixture = fixture;
	}
	
	public void setRightFixture(Fixture fixture)
	{
		rightFixture = fixture;
	}
	
	private final Set<WalkingActor> leftCollidingActors = new HashSet<WalkingActor>();
	private final Set<WalkingActor> rightCollidingActors = new HashSet<WalkingActor>();
	
	public Set<WalkingActor> getLeftCollidingActors()
	{
		return leftCollidingActors;
	}
	
	public Set<WalkingActor> getRightCollidingActors()
	{
		return rightCollidingActors;
	}
	
	private int footCollisionCount = 0;
	
	public int getFootCollisionCount()
	{
		return footCollisionCount;
	}
	
	public void setFootCollisionCount(int count)
	{
		footCollisionCount = count;
	}
	
	public boolean isOnGround()
	{
		return footCollisionCount > 0;
	}
	
	protected Sprite sprite;
	
	private boolean spriteFlipped = false;
	
	protected float drawOffsetX = 0;
	protected float drawOffsetY = 0;	
	
	//if left is true, make the sprite face left, otherwise face right
	protected void setSpriteFlipped(boolean flipped)
	{
		if ((spriteFlipped && !flipped)
			|| !spriteFlipped && flipped)
		{
			sprite.flip(true, false);
		}
		
		spriteFlipped = flipped;
	}
	
	boolean isSpriteFlipped()
	{
		return spriteFlipped;
	}
	
	@Override
	public void draw(SpriteBatch batch, float parentAlpha)
	{
		Vector2 pos = body.getPosition();
		
		float left = pos.x - sprite.getWidth() / 2;
		float top = pos.y - height / 2; // 4 because we only want to use
												// the bottom 1/2 of this sprite
		
		//use the Actor's x and y properties as an offset for the sprite
		left += drawOffsetX;
		top += drawOffsetY;
		
		sprite.setPosition(left, top);
		sprite.draw(batch);
	}
	
	//movement control vars
	protected boolean isWalking = false;
	protected float walkSpeed = 0;
	
	protected boolean isJumping = false;
	protected float jumpPower = 10;
	
	private boolean alreadyModifiedMidAirSpeed = false;
	
	//change speed based on the movement vars
	protected void doWalk()
	{
		Vector2 speed = body.getLinearVelocity();
		
		if (isWalking)
		{
			boolean sameDirection = speed.x * walkSpeed > 0;
			
			//can't change directions in mid air
			if (isOnGround())
			{
				speed.x = walkSpeed;
			}
			else if (!alreadyModifiedMidAirSpeed && !sameDirection)
			{
				speed.x += walkSpeed * 0.75f;
				
				alreadyModifiedMidAirSpeed = true;
			}
		}
		else if (isOnGround())
		{
			//if there's no walk input, don't move
			speed.x = 0;
		}
		
		if (isJumping
			&& isOnGround())
		{
			speed.y = jumpPower;
		}
		
		if (isOnGround())
		{
			alreadyModifiedMidAirSpeed = false;
		}
		
		body.setLinearVelocity(speed);
	}
	
	@Override
	public void act(float delta)
	{
		super.act(delta);
		
		Vector2 pos = body.getPosition();
		
		x = pos.x;
		y = pos.y;
	}
	
	public void setBodyPos(Vector2 newPos)
	{
		body.setTransform(newPos, 0);
	}
	
	WalkingActor(String texturePath, float bodyWidth, float bodyHeight, float mass)
	{
		super();
		
		ChameleonGame.assets.load(texturePath, Texture.class);
		ChameleonGame.assets.finishLoading();
		
		sprite = new Sprite(ChameleonGame.assets.get(texturePath, Texture.class));
		
		width = bodyWidth;
		height = bodyHeight;
		
		sprite.setSize(sprite.getTexture().getWidth() * Physics.SCREEN_TO_WORLD,
				sprite.getTexture().getHeight() * Physics.SCREEN_TO_WORLD);
		
		PolygonShape shape = new PolygonShape();
		shape.setAsBox(bodyWidth/2, bodyHeight/2);
		
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.shape = shape;
		fixtureDef.friction = 0.0f;
		
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyType.DynamicBody;
		bodyDef.position.set(0, 0);
		
		body = Physics.getWorld().createBody(bodyDef);
		Fixture fixture = body.createFixture(fixtureDef);
		body.setUserData(this);
		
		Filter filter = fixture.getFilterData();
		filter.groupIndex = -2;
		fixture.setFilterData(filter);
		
		MassData massProps = new MassData();
		massProps.center.set(new Vector2(0, 0));
		massProps.mass = mass;
		massProps.I = 0;
		body.setMassData(massProps);
		
		Physics.createFootFixture(this, body, bodyWidth, bodyHeight/2, -(bodyHeight/2));
	}
	
	@Override
	public void dispose()
	{
		Physics.getWorld().destroyBody(body);
		body = null;
		sprite = null;
	}
}
